home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
general
/
viewers
/
polyview
/
polyvw31.lha
/
Polyview3.1
/
new
/
playaiff.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-06-23
|
22KB
|
786 lines
/* ------------------------------------------------------------------------ */
/* THIS IS NOT NCSA-DEVELOPED SOFTWARE. */
/* This file is based on /usr/people/4Dgifts/examples/libaudio/playaiff.c */
/* as shipped with Silicon Graphics IRIX 4.0.2. */
/* ------------------------------------------------------------------------ */
/* $Header: /usr3/people/gbourhis/pv3/new/RCS/playaiff.c,v 1.1 92/09/18 10:55:26 marca Exp $ */
#ifdef RCSLOG
$Log: playaiff.c,v $
* Revision 1.1 92/09/18 10:55:26 marca
* Initial revision
*
#endif
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <malloc.h>
#include "aiff.h"
#include <audio.h>
/*
* we're going to nail down the value of infinity for the IEEE conversion
* routine below: math.h yields to local definition of HUGE_VAL
*/
union
{
unsigned long n[2]; /* initialized in main */
double d;
} _inf_union;
#define HUGE_VAL _inf_union.d;
#include <math.h>
typedef union
{
unsigned char b[2];
short s;
} align_short_t;
typedef union
{
unsigned char b[4];
long l;
} align_long_t;
/*
* local subroutines
*/
static void playfile(void);
static void parse_cmd_line(int argc, char **argv);
static int read_chunk_header(chunk_header_t *);
static void read_form_chunk(chunk_header_t *, form_chunk_t *);
static void read_comm_chunk(chunk_header_t *,comm_chunk_t *,
audio_params_t *audio_params);
static void init_audio(audio_params_t *, ALport *);
static void read_ssnd_chunk(chunk_header_t *, ssnd_chunk_t *);
static void play_audio_samps(comm_chunk_t *,
ssnd_chunk_t *, audio_params_t *, ALport);
static void skip_chunk(chunk_header_t *);
static double ConvertFromIeeeExtended(char *);
/*
* globals
*/
static char *filename; /* input file name */
static int fd; /* input file descriptor */
static char *myname; /* name of this program */
static int verbose; /* global flag */
static int bytes_per_samp; /* sample width */
static int samps_per_frame; /* frame size */
static int frames_per_sec; /* sample rate */
static int bytes_per_buf; /* bytes per sample buffer */
static int samps_per_buf; /* samples per sample buffer */
static int frames_per_buf; /* frames per sample buffer */
static double secs_per_frame; /* time it takes to play one audio frame */
static double secs_per_buf; /* time it takes to play one sample buffer */
static char *sampbuf; /* the sample buffer */
/* Return a 1 if we can play sound, 0 otherwise. */
int InitSound (void)
{
int n;
/* Assign name for error reports. */
myname = "PlayAFile";
/*
* initialize the inifinity union
*/
_inf_union.n[0] = 0x7ff00000;
_inf_union.n[1] = 0x00000000;
/*
* return gracefully if we're running on anything besides a
* 4D/35 or an Indigo
*/
if ((n = open("/dev/hdsp/hdsp0master", O_RDWR)) < 0)
return 0;
close(n);
return 1;
}
/* Given a filename, just try to open and play the file. */
void PlayAFile (char *fname)
{
/* Assign input filename to global variable. */
filename = fname;
/* Open and play the file. */
if ((fd = open(filename, O_RDONLY)) < 0)
return;
playfile();
close(fd);
return;
}
/*
* P L A Y F I L E
*/
static void
playfile(void)
{
int i,n;
char buf[8];
chunk_header_t chunk_header;
form_chunk_t form_data;
comm_chunk_t comm_data;
ssnd_chunk_t ssnd_data;
audio_params_t audio_params;
ALport audio_port;
if ((n = read_chunk_header(&chunk_header)) != CHUNK_HEADER)
{
fprintf(stderr, "%s: failed to read FORM chunk header\n", myname);
exit(1);
}
if (strncmp(chunk_header.id, "FORM", 4)) /* form container */
{
fprintf(stderr, "%s: couldn't find FORM chunk id\n", myname);
exit(1);
}
read_form_chunk(&chunk_header, &form_data);
/*
* loop on the local chunks
*/
while ((n = read_chunk_header(&chunk_header)) != 0)
{
if (n != CHUNK_HEADER)
{
fprintf(stderr, "%s: failed to read a chunk header\n", myname);
exit(1);
}
if (!strncmp(chunk_header.id, "COMM", 4)) /* common */
{
read_comm_chunk(&chunk_header, &comm_data, &audio_params);
}
else if (!strncmp(chunk_header.id, "SSND", 4)) /* sound data */
{
read_ssnd_chunk(&chunk_header, &ssnd_data);
}
else if ((!strncmp(chunk_header.id, "MARK", 4)) /* marker */
|| (!strncmp(chunk_header.id, "INST", 4)) /* instrument */
|| (!strncmp(chunk_header.id, "APPL", 4)) /* appl specific */
|| (!strncmp(chunk_header.id, "MIDI", 4)) /* midi data */
|| (!strncmp(chunk_header.id, "AESD", 4)) /* audio rec */
|| (!strncmp(chunk_header.id, "COMT", 4)) /* comments */
|| (!strncmp(chunk_header.id, "NAME", 4)) /* text */
|| (!strncmp(chunk_header.id, "AUTH", 4)) /* text */
|| (!strncmp(chunk_header.id, "(c) ", 4)) /* text */
|| (!strncmp(chunk_header.id, "ANNO", 4))) /* text */
{
skip_chunk(&chunk_header);
}
else
{
fprintf(stderr,
"%s: bad chunk id 0x%02x%02x%02x%02x\n",
myname, chunk_header.id[0],chunk_header.id[1],
chunk_header.id[2], chunk_header.id[3]);
}
} /* while */
/*
* play the sample data
*/
init_audio(&audio_params, &audio_port);
play_audio_samps(&comm_data, &ssnd_data, &audio_params, audio_port);
ALcloseport(audio_port);
free(sampbuf);
}
/*
* R E A D _ C H U N K _ H E A D E R
*/
static int
read_chunk_header(chunk_header_t *chunk_header)
{
align_long_t align_long;
char buf[CHUNK_HEADER];
int i;
int n;
if ((n = read(fd, buf, CHUNK_HEADER)) != CHUNK_HEADER)
{
return(n);
}
for (i=0; i<4; i++)
{
chunk_header->id[i] = buf[i];
}
for (i=0; i<4; i++)
{
align_long.b[i] = buf[i+4];
}
chunk_header->size = align_long.l;
return(CHUNK_HEADER);
}
static void
read_form_chunk(chunk_header_t *chunk_header, form_chunk_t *form_data)
{
int n;
char buf[FORM_CHUNK_DATA];
if (chunk_header->size < 0)
{
fprintf(stderr, "%s: invalid FORM chunk data size %d\n",myname,
chunk_header->size);
exit(1);
}
else if (chunk_header->size == 0)
{
fprintf(stderr,"%s: FORM chunk data size = 0\n", myname);
exit(0);
}
else
{
if (verbose)
{
fprintf(stderr,"%s: FORM chunk data size = %d\n",myname,
chunk_header->size);
}
}
if ((n = read(fd, buf, FORM_CHUNK_DATA)) != FORM_CHUNK_DATA)
{
fprintf(stderr, "%s: couldn't read AIFF identifier from %s\n",
myname, filename);
exit(1);
}
if (strncmp(buf, "AIFF", 4))
{
fprintf(stderr, "%s: %s does not have an AIFF identifier\n",
myname, filename);
exit(1);
}
else
{
if (verbose)
{
fprintf(stderr,"%s: AIFF id\n", myname);
}
}
}
/*
* R E A D _ C O M M _ C H U N K
*/
static void
read_comm_chunk(chunk_header_t *chunk_header,
comm_chunk_t *comm_data,
audio_params_t *audio_params)
{
int n;
char *buf, *bufp;
int i;
align_short_t align_short;
align_long_t align_long;
double tmpdouble;
buf = malloc(COMM_CHUNK_DATA + 1); /* leave an extra loc at the end */
if ((n = read(fd, buf, COMM_CHUNK_DATA)) != COMM_CHUNK_DATA)
{
fprintf(stderr,
"%s: failed to read COMM chunk data. Expected %d bytes, got %d.\n",
myname, COMM_CHUNK_DATA, n);
exit(1);
}
bufp = buf;
for (i=0; i<2; i++)
{
align_short.b[i] = *bufp++;
}
comm_data->nchannels = align_short.s;
for (i=0; i<4; i++)
{
align_long.b[i] = *bufp++;
}
comm_data->nsampframes = align_long.l;
for (i=0; i<2; i++)
{
align_short.b[i] = *bufp++;
}
comm_data->sampwidth = align_short.s;
/*
* the sample rate value from the common chunk is an 80-bit IEEE extended
* floating point number:
* [s bit] [15 exp bits (bias=16383)] [64 mant bits (leading 1 not hidden)]
*
*/
tmpdouble = ConvertFromIeeeExtended(bufp);
bufp+=10;
if (verbose)
{
fprintf(stderr,"%s: converted ieee extended rate value to %f\n",
myname, tmpdouble);
}
comm_data->samprate = (long)tmpdouble;
if (verbose)
{
fprintf(stderr,"%s: COMM chunk data num channels = %d\n", myname,
comm_data->nchannels);
fprintf(stderr,"%s: COMM chunk data num sample frames = %d\n",
myname,
comm_data->nsampframes);
fprintf(stderr,"%s: COMM chunk data sample size = %d\n", myname,
comm_data->sampwidth);
fprintf(stderr,"%s: COMM chunk data sample rate = %d\n", myname,
comm_data->samprate);
}
switch (comm_data->samprate)
{
case 48000:
audio_params->samprate = AL_RATE_48000;
break;
case 44100:
audio_params->samprate = AL_RATE_44100;
break;
case 32000:
audio_params->samprate = AL_RATE_32000;
break;
case 22050:
audio_params->samprate = AL_RATE_22050;
break;
case 16000:
audio_params->samprate = AL_RATE_16000;
break;
case 11025:
audio_params->samprate = AL_RATE_11025;
break;
case 8000:
audio_params->samprate = AL_RATE_8000;
break;
default:
fprintf(stderr,"%s: can't set output sample rate to %d\n",
myname, comm_data->samprate);
audio_params->samprate = AL_RATE_48000;
}
switch (comm_data->nchannels)
{
case 1:
audio_params->nchannels = AL_MONO;
break;
case 2:
audio_params->nchannels = AL_STEREO;
break;
default:
fprintf(stderr, "%s: can't handle %d channels per frame\n",
myname, comm_data->nchannels);
audio_params->nchannels = AL_STEREO;
}
switch (comm_data->sampwidth)
{
case 8:
audio_params->sampwidth = AL_SAMPLE_8;
break;
case 16:
audio_params->sampwidth = AL_SAMPLE_16;
break;
case 24:
audio_params->sampwidth = AL_SAMPLE_24;
break;
default:
fprintf(stderr, "%s: unsupported sample width %d bits\n",
myname, comm_data->nchannels);
audio_params->sampwidth = AL_SAMPLE_16;
}
free(buf);
}
/*
* I N I T _ A U D I O
*/
static void
init_audio(audio_params_t *audio_params, ALport *audio_port)
{
ALconfig audio_port_config;
long pvbuf[2];
long buflen;
/*
* set output sample rate
*/
pvbuf[0] = AL_OUTPUT_RATE;
pvbuf[1] = audio_params->samprate;
buflen = 2;
ALsetparams(AL_DEFAULT_DEVICE, pvbuf, buflen);
/*
* decide what size blocks of samples we should read from the
* AIFF file and pass to ALwritesamps
*/
switch (audio_params->sampwidth)
{
case AL_SAMPLE_8: bytes_per_samp = 1; break;
default:
case AL_SAMPLE_16: bytes_per_samp = 2; break;
}
switch (audio_params->nchannels)
{
case AL_MONO: samps_per_frame = 1; break;
default:
case AL_STEREO: samps_per_frame = 2; break;
}
switch(audio_params->samprate)
{
case AL_RATE_48000: frames_per_sec = 48000; break;
case AL_RATE_44100: frames_per_sec = 44100; break;
case AL_RATE_32000: frames_per_sec = 32000; break;
case AL_RATE_22050: frames_per_sec = 22050; break;
case AL_RATE_16000: frames_per_sec = 16000; break;
case AL_RATE_11025: frames_per_sec = 11025; break;
case AL_RATE_8000: frames_per_sec = 8000; break;
}
if (frames_per_sec % 2 == 0)
{
/*
* make the buffer large enough to hold 1/2 sec of audio frames
*/
secs_per_frame = 1.0 / ((double)frames_per_sec);
bytes_per_buf = bytes_per_samp * samps_per_frame * frames_per_sec / 2;
samps_per_buf = bytes_per_buf / bytes_per_samp;
frames_per_buf = samps_per_buf / samps_per_frame;
secs_per_buf = secs_per_frame * frames_per_buf;
sampbuf = malloc(bytes_per_buf);
}
else /* eg, can't divide 11025 by 2 to get exactly 0.5 sec of samples */
{
secs_per_frame = 1.0 / ((double)frames_per_sec);
bytes_per_buf = bytes_per_samp * samps_per_frame*(frames_per_sec+1)/2;
samps_per_buf = bytes_per_buf / bytes_per_samp;
frames_per_buf = samps_per_buf / samps_per_frame;
secs_per_buf = secs_per_frame * frames_per_buf;
sampbuf = malloc(bytes_per_buf);
}
/*
* configure and open audio port
*/
audio_port_config = ALnewconfig();
ALsetwidth(audio_port_config, audio_params->sampwidth);
ALsetchannels(audio_port_config, audio_params->nchannels);
/*
* make the ring buffer large enough to hold 1 sec of audio samples
*/
ALsetqueuesize(audio_port_config, samps_per_buf*2);
*audio_port = ALopenport(myname, "w", audio_port_config);
}
static void
read_ssnd_chunk(chunk_header_t *chunk_header, ssnd_chunk_t *ssnd_data)
{
char buf[SSND_CHUNK_DATA];
int i;
align_long_t align_long;
read(fd, buf, SSND_CHUNK_DATA);
for (i=0; i<4; i++)
{
align_long.b[i] = buf[i];
}
ssnd_data->offset = align_long.l;
for (i=0; i<4; i++)
{
align_long.b[i] = buf[i+4];
}
ssnd_data->blocksize = align_long.l;
if (verbose)
{
fprintf(stderr, "%s: SSND data offset=%d blocksize=%d\n",
myname, ssnd_data->offset, ssnd_data->blocksize);
}
/*
* store the offset to the beginning of the audio sample data so that
* we can come back and play it later
*/
ssnd_data->file_position = lseek(fd, 0, SEEK_CUR);
ssnd_data->sample_area_bytes = chunk_header->size - 2*sizeof(long);
if (chunk_header->size %2 == 1)
{
if (verbose)
{
fprintf(stderr,
"%s: SSND data: odd size %d; assume there's a trailing pad byte\n",
myname, chunk_header->size);
}
ssnd_data->sample_area_bytes++;
}
/*
* move the fileptr to the end of the chunk
*/
lseek(fd, ssnd_data->sample_area_bytes, SEEK_CUR);
}
/*
* P L A Y _ A U D I O _ S A M P S
*/
static void
play_audio_samps(
comm_chunk_t *comm_data,
ssnd_chunk_t *ssnd_data,
audio_params_t *audio_params,
ALport audio_port)
{
int num_bufs;
int leftover_bytes;
int leftover_samps;
int leftover_frames;
int samp_count;
int frame_count;
double sec_count;
int i;
int bytes_read;
int samples_read;
int done;
int total_frames;
int total_samps;
int total_samp_bytes;
if (verbose)
{
fprintf(stderr,
"%s: play SSND chunk sample section = %d bytes\n",
myname, ssnd_data->sample_area_bytes);
}
/*
* figure out how many reads we have to do
*/
total_frames = comm_data->nsampframes;
total_samps = total_frames * samps_per_frame;
total_samp_bytes = total_samps * bytes_per_samp;
num_bufs = total_samp_bytes / bytes_per_buf;
leftover_bytes = total_samp_bytes % bytes_per_buf;
leftover_samps = leftover_bytes / bytes_per_samp;
leftover_frames = leftover_samps / samps_per_frame;
if (verbose)
{
fprintf(stderr,
"%s: play data: total sample bytes = %d total frames = %d\n",
myname, total_samp_bytes, total_frames);
fprintf(stderr, "%s: play data: %d bytes/blk =%d frames/blk\n",
myname, bytes_per_buf, frames_per_buf);
fprintf(stderr,
"%s: play data: blocks = %d leftover samps=%d frames=%d\n",
myname, num_bufs, leftover_samps, leftover_frames);
}
/*
* move the fileptr to the beginning of the sample data
*/
lseek(fd, ssnd_data->file_position, SEEK_SET);
/*
* note that there may be some pad bytes following the valid samples -
* for example, the sample data area may be padded so that the valid
* samples begin on a block boundary and the sample area ends on a block
* boundary (where blocksize is specified by the user)
*/
done = 0;
samp_count = 0;
frame_count = 0;
sec_count = 0.0;
for (i=0; i<num_bufs; i++)
{
samp_count += samps_per_buf;
frame_count += frames_per_buf;
sec_count += secs_per_buf;
if (verbose)
{
fprintf(stderr, "%s: %d sample frames %6.2f secs\n",
myname, frame_count, sec_count);
}
if ((bytes_read = read(fd, sampbuf, bytes_per_buf)) < bytes_per_buf)
{
if (verbose)
{
fprintf(stderr, "%s: short read on audio sample data\n",
myname);
}
done++;
}
samples_read = bytes_read / bytes_per_samp;
ALwritesamps(audio_port, sampbuf, samples_read);
if (done)
{
/*
* allow the audio buffer to drain
*/
while(ALgetfilled(audio_port) > 0)sginap(1);
return;
}
}
/*
* play the leftovers
*/
samp_count += leftover_samps;
frame_count += leftover_frames;
sec_count += ((double)leftover_frames) * secs_per_frame;
if (verbose && leftover_samps>0)
{
fprintf(stderr, "%s: %d sample frames %6.2f secs\n",
myname, frame_count, sec_count);
}
if ((bytes_read = read(fd, sampbuf, leftover_bytes)) < leftover_bytes)
{
if (verbose)
{
fprintf(stderr,
"%s: short read: expected %d sample bytes, got %d bytes\n",
myname, leftover_bytes, bytes_read);
}
}
samples_read = bytes_read / bytes_per_samp;
ALwritesamps(audio_port, sampbuf, samples_read);
/*
* allow the audio buffer to drain
*/
while(ALgetfilled(audio_port) > 0)sginap(1);
return;
}
/*
* S K I P _ C H U N K
*/
static void
skip_chunk(chunk_header_t *chunk_header)
{
char id[5];
int s;
strncpy(id, chunk_header->id, 4);
id[4] = '\0';
if (verbose)
{
fprintf(stderr, "%s: %s chunk data size = %d bytes skipped\n",
myname, id, chunk_header->size);
}
/* skip the pad byte, if necessary */
s = ((chunk_header->size % 2) == 1) ? chunk_header->size + 1 : chunk_header->size;
lseek(fd, s, SEEK_CUR);
}
/*
* C O N V E R T F R O M I E E E E X T E N D E D
*/
/*
* Copyright (C) 1988-1991 Apple Computer, Inc.
* All rights reserved.
*
* Warranty Information
* Even though Apple has reviewed this software, Apple makes no warranty
* or representation, either express or implied, with respect to this
* software, its quality, accuracy, merchantability, or fitness for a
* particular purpose. As a result, this software is provided "as is,"
* and you, its user, are assuming the entire risk as to its quality
* and accuracy.
*
* This code may be used and freely distributed as long as it includes
* this copyright notice and the above warranty information.
* Machine-independent I/O routines for IEEE floating-point numbers.
*
* NaN's and infinities are converted to HUGE_VAL or HUGE, which
* happens to be infinity on IEEE machines. Unfortunately, it is
* impossible to preserve NaN's in a machine-independent way.
* Infinities are, however, preserved on IEEE machines.
*
* These routines have been tested on the following machines:
* Apple Macintosh, MPW 3.1 C compiler
* Apple Macintosh, THINK C compiler
* Silicon Graphics IRIS, MIPS compiler
* Cray X/MP and Y/MP
* Digital Equipment VAX
*
*
* Implemented by Malcolm Slaney and Ken Turkowski.
*
* Malcolm Slaney contributions during 1988-1990 include big- and little-
* endian file I/O, conversion to and from Motorola's extended 80-bit
* floating-point format, and conversions to and from IEEE single-
* precision floating-point format.
*
* In 1991, Ken Turkowski implemented the conversions to and from
* IEEE double-precision format, added more precision to the extended
* conversions, and accommodated conversions involving +/- infinity,
* NaN's, and denormalized numbers.
*/
#ifndef HUGE_VAL
# define HUGE_VAL HUGE
#endif /* HUGE_VAL */
# define UnsignedToFloat(u) \
(((double)((long)(u - 2147483647L - 1))) + 2147483648.0)
/****************************************************************
* Extended precision IEEE floating-point conversion routine.
****************************************************************/
static double
ConvertFromIeeeExtended(char *bytes)
{
double f;
long expon;
unsigned long hiMant, loMant;
expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24)
| ((unsigned long)(bytes[3] & 0xFF) << 16)
| ((unsigned long)(bytes[4] & 0xFF) << 8)
| ((unsigned long)(bytes[5] & 0xFF));
loMant = ((unsigned long)(bytes[6] & 0xFF) << 24)
| ((unsigned long)(bytes[7] & 0xFF) << 16)
| ((unsigned long)(bytes[8] & 0xFF) << 8)
| ((unsigned long)(bytes[9] & 0xFF));
if (expon == 0 && hiMant == 0 && loMant == 0) {
f = 0;
}
else {
if (expon == 0x7FFF) { /* Infinity or NaN */
f = HUGE_VAL;
}
else {
expon -= 16383;
f = ldexp(UnsignedToFloat(hiMant), expon-=31);
f += ldexp(UnsignedToFloat(loMant), expon-=32);
}
}
if (bytes[0] & 0x80)
return -f;
else
return f;
}